#============================================================
#	Platform Path
#============================================================
#  平台移动点路径
#============================================================
# @datetime: 2022-3-26 16:01:52
#============================================================

class_name PlatformPathPoint
extends Node2D


##  边缘点
enum {
	NONE = 0,
	WALL = 1,
	DROP = 2,
}

enum CellDir {
	LEFT = 0,
	RIGHT = 1,
}

const TEST = preload("TEST.tscn")


export var map : NodePath = @""
export var cell_size : Vector2 = Vector2(64, 64)
export var show_lines : bool = false


var jump_height : float = 200.0 / 60.0
var jump_distance : float = 400.0 / 60.0

var _points : Dictionary = {}
var _graph : AStar2D = AStar2D.new()


onready var _tilemap : TileMap = get_node(map) as TileMap


#============================================================
#   Set/Get
#============================================================
func has_point(point: Vector2) -> bool:
	return _points.has(point)

func world_to_map(point: Vector2) -> Vector2:
	return _tilemap.world_to_map(point)

func map_to_world(point: Vector2) -> Vector2:
	return _tilemap.map_to_world(point)



#============================================================
#   内置
#============================================================
func _draw():
	if not show_lines:
		return
	return
	var points = _graph.get_points()
	for point in points:
		var closestRight = -1
		var closestLeftDrop = -1
		var closestRightDrop = -1
		var pos = _graph.get_point_position(point)
		var stat = _drop_point(pos, true, true)

		var pointsToJoin = []
		var noBiJoin = []

		var cell_size = self.cell_size.x
		for newPoint in points:
			var newPos = _graph.get_point_position(newPoint)
			if (stat[CellDir.RIGHT] == 0 
				and newPos[CellDir.RIGHT] == pos[CellDir.RIGHT] 
				and newPos[CellDir.LEFT] > pos[0]
			):
				if closestRight < 0 or newPos[0] < _graph.get_point_position(closestRight)[0]: 
					closestRight = newPoint
			if (stat[0] == DROP):
				if (newPos[0] == pos[0] - cell_size and newPos[1] > pos[1]):
					if closestLeftDrop < 0 or newPos[1] < _graph.get_point_position(closestLeftDrop)[1]:
						closestLeftDrop = newPoint
				if (newPos[1] >= pos[1] - (cell_size * jump_height) and newPos[1] <= pos[1] and 
					newPos[0] > pos[0] - (cell_size * (jump_distance + 2)) and newPos[0] < pos[0]) and _drop_point(newPos, true, true)[1] == DROP :
						pointsToJoin.append(newPoint)
			if (stat[1] == DROP):
				if (newPos[0] == pos[0] + cell_size and newPos[1] > pos[1]):
					if closestRightDrop < 0 or newPos[1] < _graph.get_point_position(closestRightDrop)[1]:
						closestRightDrop = newPoint
				if (newPos[1] >= pos[1] - (cell_size * jump_height) and newPos[1] <= pos[1] and 
					newPos[0] < pos[0] + (cell_size * (jump_distance + 2)) and newPos[0] > pos[0]) and _drop_point(newPos, true, true)[0] == DROP :
						pointsToJoin.append(newPoint)

		if (closestRight > 0):
			pointsToJoin.append(closestRight)
		if (closestLeftDrop > 0):
			if (_graph.get_point_position(closestLeftDrop)[1] == pos[1] + cell_size):
				pointsToJoin.append(closestLeftDrop)
			else:
				noBiJoin.append(closestLeftDrop)
		if (closestRightDrop > 0):
			if (_graph.get_point_position(closestRightDrop)[1] == pos[1] + cell_size):
				pointsToJoin.append(closestRightDrop)
			else:
				noBiJoin.append(closestRightDrop)

		for joinPoint in pointsToJoin:
			draw_line(pos, _graph.get_point_position(joinPoint), Color(255, 0, 0), 1)
		for joinPoint in noBiJoin:
			draw_line(pos, _graph.get_point_position(joinPoint), Color(255, 0, 0), 1)



#============================================================
#   自定义
#============================================================
## 开始扫描
func scan(points: Array):
	for point in points:
		_points[point] = null
	
	_scan_point()
	_create_connections()

##  判断边缘点
## @point  要判断的点
## @global  这个 point 是一个全局的点，需要转为 tilemap 上的点
## @is_above  这个 point 是一个上面的点，加上 Vector2(0, -1)
## @return  返回左右方向是否有落脚点
func _drop_point(point: Vector2, global = false, is_above = false) -> Vector2:
	if global:
		point = world_to_map(point)
	if is_above:
		point = point + Vector2.UP
	
	var result = Vector2(0,0)
	# 如果这个点上方存在其他点，则返回 Vector(0,0) 向量
	if has_point(point + Vector2.UP):
		return result
	
	# 左上角存在点，则是墙
	if has_point(point + Vector2(-1, -1)):
		result[CellDir.LEFT] = WALL
	# 否则如果左侧不存在点，则是可落脚点
	elif not has_point(point + Vector2.LEFT):
		result[CellDir.LEFT] = DROP
	
	# 右上角存在点，则是墙
	if has_point(point + Vector2(1, -1)):
		result[CellDir.RIGHT] = WALL
	# 否则如果右侧不存在点，则是可落脚点
	elif not has_point(point + Vector2.RIGHT):
		result[CellDir.RIGHT] = DROP
	
	return result


##  扫描点
func _scan_point():
	var space_state = get_world_2d().direct_space_state
	for point in _points:
		var drop = _drop_point(point)
		
		# 判断左右是否有落脚点，都没有的话，跳到下一个循环
		if drop == Vector2(NONE, NONE):
			continue
		
		# 创建位置点
		_create_point(point)
		
		# 左边
		if drop[CellDir.LEFT] == DROP:
			var from = map_to_world(point + Vector2.LEFT)
			var to = from + Vector2(0, 1000)
			# 当前位置是可落到底部的位置点
			var result = space_state.intersect_ray(from, to)
			if result:
				_create_point(world_to_map(result.position))
		
		# 右边
		if drop[CellDir.RIGHT] == DROP:
			var from = map_to_world(point + Vector2.RIGHT)
			var to = from + Vector2(0, 1000)
			var result = space_state.intersect_ray(from, to)
			if result:
				_create_point(world_to_map(result.position))


##  创建左右移动点
## @point  map 上的位置点
func _create_point(point: Vector2):
	var above = point + Vector2.UP
	# 转为 world 位置
	var world_point = map_to_world(above) + cell_size / 2
	if (_graph.get_points() 
		and _graph.get_point_position(_graph.get_closest_point(world_point)) == world_point
	):
		return
	if (show_lines):
		var test = TEST.instance()
		test.set_position(world_point)
		call_deferred("add_child", test)
	
	_graph.add_point(_graph.get_available_point_id(), world_point)


##  建立连接
func _create_connections():
	var points_id = _graph.get_points()
	
	## 扫描 AStar 中的点
	for point_id in points_id:
		var point : Vector2 = _graph.get_point_position(point_id)
		var edge : Vector2 = _drop_point(point, true, true)
		
		# 遍历这个点与其他点的关系
		for p2_id in points_id:
			var new_point = _graph.get_point_position(p2_id)
			# 右侧没有或者两个 id 一样
			if (edge[CellDir.RIGHT] == NONE
				or point_id == p2_id
				
				# 并且 x 
				and point.x > new_point.x
			):
				
				# （我自己写的，没写完，自己思考理解下面的代码逻辑）
				pass
				
	
	for id in points_id:
		var point : Vector2 = _graph.get_point_position(id)
		var edge : Vector2 = _drop_point(point, true, true)	# 两边是否有落脚点
		
		var closest_right_id : int = -1	# 最近的右边的点的 id
		var closest_left_drop_id : int = -1	# 最近的左边的落脚点id
		var closest_right_drop_id : int = -1	# 最近的右边的落脚点id
		
		var point_to_join : Array = []
		var no_bi_join : Array = []
		
		for new_id in points_id:
			var new_point : Vector2 = _graph.get_point_position(new_id)
			# 右侧没有
			if (edge[1] == NONE
				or point[0] == new_point[0]
				# new_point 在 point 节点的上面
				and point[0] > new_point[0]
			):
				if (closest_right_id < 0 
					# 这个点的 x 值 比 右侧最近的点的 x 值小
					or new_point[0] < _graph.get_point_position(closest_right_id)[0]
				): 
					closest_right_id = new_id
			
			# 左侧是可落脚点
			if (edge[0] == DROP):
				if (new_point[0] == point[0] - cell_size[0] 
					and new_point[1] > point[1]
				):
					if (closest_left_drop_id < 0 
						or new_point[1] < _graph.get_point_position(closest_left_drop_id)[1]
					):
						closest_left_drop_id = new_id
				if (
					(new_point[1] >= point[1] - (cell_size[1] * jump_height) 
						and new_point[1] <= point[1]
						and  new_point[0] > point[0] - (cell_size[0] * (jump_distance + 2)) 
						and new_point[0] < point[0]
					) 
					and _drop_point(new_point, true, true)[1] == DROP
				):
					point_to_join.append(new_id)
			
			if (edge[1] == DROP):
				if (new_point[0] == point[0] + cell_size[0] 
					and new_point[1] > point[1]
				):
					if (closest_right_drop_id < 0 
						or new_point[1] < _graph.get_point_position(closest_right_drop_id)[1]
					):
						closest_right_drop_id = new_id
				if (
					(new_point[1] >= point[1] - (cell_size[1] * jump_height) 
						and new_point[1] <= point[1] 
						and new_point[0] < point[0] + (cell_size[0] * (jump_distance + 2)) 
						and new_point[0] > point[0]
					) 
					and _drop_point(new_point, true, true)[0] == DROP
				):
						point_to_join.append(new_id)
		
		if (closest_right_id > 0):
			point_to_join.append(closest_right_id)
		if (closest_left_drop_id > 0):
			if (_graph.get_point_position(closest_left_drop_id)[1] == point[1] + cell_size[1]):
				point_to_join.append(closest_left_drop_id)
			else:
				no_bi_join.append(closest_left_drop_id)
		if (closest_right_drop_id > 0):
			if (_graph.get_point_position(closest_right_drop_id)[1] == point[1] + cell_size[1]):
				point_to_join.append(closest_right_drop_id)
			else:
				no_bi_join.append(closest_right_drop_id)
			
	

